import {
  isArray,
  isObjectType,
} from '@/library/tool-kits/is-valid'
import { getType } from '../get-type'

const { hasOwnProperty } = Object.prototype

export type ExtendArg = Record<keyof any, any>

/**
 * @description 合并多个对象到target对象 - 当多个对象的同一个键类型不一致时，直接覆盖。
 * @param {boolean} isDeep 开启深复制. default: `false`
 * @param {ExtendArg[]} args 第一个为目标对象, 其余的为要合并的对象. 将要合并的对象仅支持普通对象、数组、函数
 * @returns 目标对象
 * @see `shallowExtend | deepExtend`
 */
export function extend(isDeep = false, ...args: ExtendArg[]): ExtendArg {
  let options: ExtendArg
  let optionsType
  let name
  let src
  let copy
  let clone
  const { length } = args
  let i = 1
  let target = args[0] ?? {}

  const type = getType(target)
  // 如果target不是对象、数组或函数, 无法进行复制, 默认设为{}
  if (type !== 'object' && type !== 'function' && type !== 'array') {
    target = {}
  }

  for (; i < length; i += 1) {
    options = args[i]
    optionsType = getType(options)
    if (
      optionsType === 'object'
      || optionsType === 'array'
      || optionsType === 'function'
    ) {
      for (name in options) {
        if (hasOwnProperty.call(options, name)) {
          src = target[name]
          copy = options[name]
          // eslint-disable-next-line no-continue
          if (target === copy) continue // 解决循环引用
          if (isDeep && copy && (isObjectType(copy) || isArray(copy))) {
            if (isArray(copy)) {
              // 要复制的对象属性值类型需要与目标属性值相同, 否则覆盖前边的值
              clone = src && Array.isArray(src) ? src : []
            } else {
              clone = src && isObjectType(src) ? src : {}
            }
            target[name] = extend(isDeep, clone, copy)
          } else {
            target[name] = copy
          }
        }
      }
    }
  }

  return target
}
